# See LICENSE for license details.

#*****************************************************************************
# napot.S
#-----------------------------------------------------------------------------
#
# Test Svnapot
#

#include "riscv_test.h"
#include "test_macros.h"

#if (DRAM_BASE >> 30 << 30) != DRAM_BASE
# error This test requires DRAM_BASE be SV39 superpage-aligned
#endif

#if __riscv_xlen != 64
# error This test requires RV64
#endif

RVTEST_RV64M
RVTEST_CODE_BEGIN

  # Construct the page table

#define MY_VA 0x40201010
  # VPN 2 == VPN 1 == VPN 0 == 0x1
  # Page offset == 0x10

  ####

  # Level 0 PTE contents

  # PPN
  la a0, my_data
  srl a0, a0, 12

  # adjust the PPN to be in NAPOT form
  li a1, ~0xF
  and a0, a0, a1
  ori a0, a0, 0x8

  # attributes
  sll a0, a0, PTE_PPN_SHIFT
  li a1, PTE_V | PTE_U | PTE_R | PTE_W | PTE_X | PTE_A | PTE_D | PTE_N
  or a0, a0, a1

  # Level 0 PTE address
  la a1, page_table
  addi a1, a1, ((MY_VA >> 12) & 0x1FF) * 8

  # Level 0 PTE store
  sd a0, (a1)

  ####

  # Level 1 PTE contents
  la a0, page_table
  srl a0, a0, 12
  sll a0, a0, PTE_PPN_SHIFT
  li a1, PTE_V
  or a0, a0, a1

  # Level 1 PTE address
  la a1, page_table
  addi a1, a1, ((MY_VA >> 21) & 0x1FF) * 8
  li a2, 1 << 12
  add a1, a1, a2

  # Level 1 PTE store
  sd a0, (a1)

  ####

  # Level 2 PTE contents
  la a0, page_table
  li a1, 1 << 12
  add a0, a0, a1
  srl a0, a0, 12
  sll a0, a0, PTE_PPN_SHIFT
  li a1, PTE_V
  or a0, a0, a1

  # Level 2 PTE address
  la a1, page_table
  addi a1, a1, ((MY_VA >> 30) & 0x1FF) * 8
  li a2, 2 << 12
  add a1, a1, a2

  # Level 2 PTE store
  sd a0, (a1)

  ####

  # Do a load from the PA that would be written if the PTE were misinterpreted as non-NAPOT
  la a0, my_data
  li a1, ~0xFFFF
  and a0, a0, a1
  li a1, 0x8000 | (MY_VA & 0xFFF)
  or a3, a0, a1
  li a1, 0
  sw a1, (a3)

  ####
  li TESTNUM, 1

  ## Turn on VM
  la a1, page_table
  li a2, 2 << 12
  add a1, a1, a2
  srl a1, a1, 12
  li a0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39
  or a0, a0, a1
  csrw satp, a0
  sfence.vma

  # Set up MPRV with MPP=S and SUM=1, so loads and stores use S-mode and S can access U pages
  li a1, ((MSTATUS_MPP & ~(MSTATUS_MPP<<1)) * PRV_S) | MSTATUS_MPRV | MSTATUS_SUM
  csrs mstatus, a1

  # Do a store to MY_VA
  li a0, MY_VA
  li a1, 42
napot_store:
  sw a1, (a0)

  # Clear MPRV
  li a1, MSTATUS_MPRV
  csrc mstatus, a1

  # Do a load from the PA that would be written if the PTE were misinterpreted as non-NAPOT
  lw a1, (a3)

  # Check the result
  li a0, 42
  beq a1, a0, die

  # Do a load from the PA for MY_VA
  la a0, my_data
  li a1, MY_VA & 0xFFFF
  add a0, a0, a1
  lw a1, (a0)
  li a2, 42

  # Check the result
  bne a1, a2, die

  ####

  RVTEST_PASS

  TEST_PASSFAIL

  .align 2
  .global mtvec_handler
mtvec_handler:
  # Skip if Svnapot is not implemented.
  csrr t5, mcause
  li t6, CAUSE_STORE_PAGE_FAULT
  bne t5, t6, die
  csrr t5, mepc
  la t6, napot_store
  bne t5, t6, die
  csrr t5, mtval
  li t6, MY_VA
  beq t5, t6, pass
die:
  RVTEST_FAIL

RVTEST_CODE_END

  .data
RVTEST_DATA_BEGIN

  TEST_DATA

.align 20
page_table: .dword 0

.align 20
my_data: .dword 0

RVTEST_DATA_END
